
#include <BStream>
#include "member_BSonDocument.h"

using namespace BWE;

member_BSonDocument::member_BSonDocument(BSonDocument* sondocument)
{
	boss = sondocument;
	last = 0;
}
member_BSonDocument::~member_BSonDocument()
{

}

const SonFormat* member_BSonDocument::nodeFormat(const BSonNode* node)
{
	member_BSonNode* sonmem = son_member(node);
	if (!sonmem->format.text.empty())
		return &sonmem->format;
	return &format;
}
void member_BSonDocument::makeFormat(BSonNode* node)
{
	if (node)
	{
		member_BSonNode* mem = son_member(node);
		mem->format.toString();

		BArray<BSonNodeHolder>& children = mem->children;
		for (int i = 0; i < children.size(); i++)
		{
			BSonNode* child = children[i];
			makeFormat(child);
		}
	}
}
bool member_BSonDocument::appendText(BSonNode* node, BString& text, int level)
{
	text.trimmed();
	if (text.size())
	{
		removeTextIndent(text, level + 1);
		son_member(node)->text << text;
		text.reset();
		return true;
	}
	return false;
}
bool member_BSonDocument::removeTextIndent(BString& text, int level)
{
	if (text.size() && level > 0)
	{
		int pos = 0;
		while (++pos < text.size())
		{
			if (text[pos] == '\n')
			{
				int count = level;
				int next = pos + 1;
				while (count > 0 && text[next] == '\t')
				{
					text.remove(next);
					count--;
				}
			}
		}
	}
	return true;
}

bool member_BSonDocument::readComment(BSonNode* node, const BString& sontext, int& pos)
{
	if (node->name() == "//")
	{
		int end = sontext.find('\n', pos);
		sontext.substr(son_member(node)->text, pos, end);
		pos = end;
		return true;
	}
	if (node->name() == "/*")
	{
		int begin = pos;
		char prev = 0;
		while (pos < sontext.size())
		{
			char code = sontext[pos++];
			if (code == '\n' && pos > linePos)
			{
				lineNumber++;
				linePos = pos;
				continue;
			}
			if (code == '/' && prev == '*')
			{
				sontext.substr(son_member(node)->text, begin, pos - 2);
				return true;
			}
			prev = code;
		}
		return true;
	}
	return false;
}
bool member_BSonDocument::readNode(BSonNode* node, const BString& sontext, int &pos, int level)
{
	BString text;
	BString value;
	while (pos < sontext.size())
	{
		char code = sontext[pos++];
		if (code == '\n' && pos > linePos)
		{
			lineNumber++;
			linePos = pos;
		}
		if (code == '\\')
		{
			text.append(sontext(pos++));
			continue;
		}
		if (code == '/' && text.endWith('/'))
		{
			BSonNode* child = new BSonNode("//");
			son_member(child)->document = boss;
			readComment(child, sontext, pos);
			node->addChild(child);
			continue;
		}
		if (code == '=')
		{
			value.reset();
			while (pos < sontext.size())
			{
				code = sontext[pos++];
				if (code == '\\')
				{
					value.append(sontext(pos++));
					continue;
				}
				if (code == ';')
					break;
				if (code == '}')
				{
					pos--;
					break;
				}
				value.append(code);
			}
			text.trimmed();
			if (text.empty())
			{
				log << "name is empty : " << "=" << value;
				return false;
			}
			value.trimmed();
			node->setAttrib(text, value);
			text.reset();
			continue;
		}
		if (code == '{')
		{
			text.trimmed();
			BSonNode* child = new BSonNode(text);
			son_member(child)->document = boss;
			readNode(child, sontext, pos, level + 1);
			node->addChild(child);
			text.reset();
			continue;
		}
		if (code == ';')
		{
			appendText(node, text, level);
			continue;
		}
		if (code == '}')
		{
			appendText(node, text, level);
			return true;
		}
		text.append(code);
	}
	return false;
}
void member_BSonDocument::writeNode(const BSonNode* node, BString& sontext, int level)
{
	member_BSonNode* sonmem = son_member(node);
	const SonFormat* fmt = nodeFormat(node);

	if (last == '\n')
		sontext.append('\t', level);
	sontext << node->name() << fmt->head_prev;
	last = fmt->head_prev;

	if (last == '\n')
		sontext.append('\t', level);
	sontext << '{' << fmt->head_post;
	last = fmt->head_post;

	for (auto it = sonmem->attribMap.begin(); it.valid(); ++it)
	{
		if (last == '\n')
			sontext.append('\t', level + 1);
		sontext << it.key() << fmt->equal_prev << '=' << fmt->equal_post << it.value() << ';' << fmt->semic_post;
		last = fmt->semic_post;
	}

	if (sonmem->children.size())
	{
		for (int i = 0; i < node->childCount(); i++)
		{
			const BSonNode* child = node->child(i);
			writeNode(child, sontext, level + 1);
		}
	}

	if (sonmem->text.size())
	{
		writeText(sonmem->text, sontext, level);
		last = sonmem->text.last();
	}

	if (fmt->tail_prev && fmt->tail_prev != last)
	{
		sontext << fmt->tail_prev;
		last = fmt->tail_prev;
	}
	if (last == '\n')
	{
		sontext.append('\t', level);
	}
	sontext << '}' << fmt->tail_post;
	last = fmt->tail_post;
}
void member_BSonDocument::writeText(const BString& text, BString& sontext, int level)
{
	if (last == '\n')
		sontext.append('\t', level + 1);
	int pos = 0;
	auto prev = text.end();
	for (auto it = text.begin(); it.valid(); ++it)
	{
		if (it == '{' || it == '=' || it == ';' || it == '}')
			sontext.append('\\');
		if (it == '/' && prev == '/')
			sontext.append('\\');
		sontext.append(*it);
		if (last == '\n' && it == last)
			sontext.append('\t', level + 1);
		prev = it;
	}
}

bool member_BSonDocument::readComment(BSonNode* node, const BStream& stream)
{
	if (node->name() == "//")
	{
		stream.readLine(son_member(node)->text);
		const BString& text = son_member(node)->text;
		return true;
	}
	if (node->name() == "/*")
	{
		int pos = stream.offset();
		int begin = pos;
		char prev = 0;
		char c = 0;
		int end = pos;
		while (!stream.end())
		{
			char code = 0;
			stream.read(code);
			if (code == '\n')
			{
				end = stream.offset();
				break;
			}
			if (code == '\n' && pos > linePos)
			{
				lineNumber++;
				linePos = pos;
				continue;
			}
			if (code == '/' && prev == '*')
			{
				int size = pos - 2 - begin;
				son_member(node)->text.reserve(size);
				stream.seek(begin);
				stream.readText(son_member(node)->text, size);
				stream.move(2);
				return true;
			}
			prev = code;
		}
		return true;
	}
	return false;
}
bool member_BSonDocument::readNode(BSonNode* node, const BStream& stream, int level)
{
	BString text;
	BString value;
	char code = 0;
	while (stream.read(code))
	{
		if (code == '\n' && stream.offset() > linePos)
		{
			lineNumber++;
			linePos = stream.offset();
		}
		if (code == '\\')
		{
			stream.read(code);
			text.append(code);
			continue;
		}
		if (code == '/' && text.endWith('/'))
		{
			BSonNode* child = new BSonNode("//");
			son_member(child)->document = boss;
			readComment(child, stream);
			text.reset();
			continue;
		}
		if (code == '=')
		{
			value.reset();
			while (stream.read(code))
			{
				if (code == ';')
					break;
				if (code == '}')
				{
					stream.seek(-1);
					break;
				}
				value.append(code);
			}
			text.trimmed();
			if (text.empty())
			{
				log << "name is empty : " << "=" << value;
				return false;
			}
			value.trimmed();
			node->setAttrib(text, value);
			text.reset();
			continue;
		}
		if (code == '{')
		{
			text.trimmed();
			BSonNode* child = new BSonNode(text);
			son_member(child)->document = boss;
			readNode(child, stream, level + 1);
			node->addChild(child);
			text.reset();
			continue;
		}
		if (code == ';')
		{
			appendText(node, text, level);
			continue;
		}
		if (code == '}')
		{
			appendText(node, text, level);
			return true;
		}
		text.append(code);
	}
	return false;
}
void member_BSonDocument::writeNode(const BSonNode* node, BStream& stream, int level)
{
	member_BSonNode* sonmem = son_member(node);
	const SonFormat* fmt = nodeFormat(node);

	if (last == '\n')
		stream.write('\t', level);
	stream << node->name() << fmt->head_prev;
	last = fmt->head_prev;

	if (last == '\n')
		stream.write('\t', level);
	stream << '{' << fmt->head_post;
	last = fmt->head_post;

	for (auto it = sonmem->attribMap.begin(); it.valid(); ++it)
	{
		if (last == '\n')
			stream.write('\t', level + 1);
		stream << it.key() << fmt->equal_prev << '=' << fmt->equal_post << it.value() << ';' << fmt->semic_post;
		last = fmt->semic_post;
	}

	if (sonmem->children.size())
	{
		for (int i = 0; i < node->childCount(); i++)
		{
			const BSonNode* child = node->child(i);
			writeNode(child, stream, level + 1);
		}
	}

	if (sonmem->text.size())
	{
		writeText(sonmem->text, stream, level);
		last = sonmem->text.last();
	}

	if (fmt->tail_prev && fmt->tail_prev != last)
	{
		stream << fmt->tail_prev;
		last = fmt->tail_prev;
	}
	if (last == '\n')
	{
		stream.write('\t', level);
	}
	stream << '}' << fmt->tail_post;
	last = fmt->tail_post;
}
void member_BSonDocument::writeText(const BString& text, BStream& stream, int level)
{
	if (last == '\n')
		stream.write('\t', level + 1);
	int pos = 0;
	auto prev = text.end();
	for (auto cit = text.begin(); cit.valid(); ++cit)
	{
		if (cit == '{' || cit == '=' || cit == ';' || cit == '}')
			stream.write('\\');
		if (cit == '/' && prev == '/')
			stream.write('\\');
		stream.write(cit->str(), cit->num());
		if (last == '\n' && cit == last)
			stream.write('\t', level + 1);
	}
}

